package com.lntea.netty.im;

import com.lntea.netty.im.codec.PacketDecoder;
import com.lntea.netty.im.codec.PacketEncoder;
import com.lntea.netty.im.handler.ClientLoginHandler;
import com.lntea.netty.im.handler.LoginResponseHandler;
import com.lntea.netty.im.handler.MessageResponseHandler;
import com.lntea.netty.im.handler.client.ClientHandler;
import com.lntea.netty.im.packet.LoginRequestPacket;
import com.lntea.netty.im.packet.MessageRequestPacket;
import com.lntea.netty.im.packet.common.LoginUtil;
import com.lntea.netty.im.codec.PacketCodeC;
import com.lntea.netty.im.packet.common.SessionUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class IMClient {

    private static final Integer MAX_RETRY = 5;

    public static void main(String[] args) {
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        Bootstrap bootstrap = new Bootstrap();
        bootstrap
                .group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // ch.pipeline().addLast(new FirstClientHandler());
                        // ch.pipeline().addLast(new ClientHandler());
                        ch.pipeline()
                                .addLast(new PacketDecoder())
                                // .addLast(new ClientLoginHandler())
                                .addLast(new LoginResponseHandler())
                                .addLast(new MessageResponseHandler())
                                .addLast(new PacketEncoder());
                    }
                });

        connect(bootstrap, "127.0.0.1", 9999, MAX_RETRY);
    }

    /**
     * 连接
     * @param bootstrap
     */
    private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
        bootstrap.connect(host, port)
                .addListener(future -> {
                    if (future.isSuccess()) {
                        System.out.println("连接成功");
                        Channel channel = ((ChannelFuture) future).channel();
                        // 启动控制台线程
                        // 输入指令控制
                        startConsoleThread(channel);
                    } else if (retry == 0) {
                        System.err.println("重试次数已用完，放弃连接");
                    }else {
                        // 第几次重连
                        int order = (MAX_RETRY - retry) + 1;
                        // 重试时间间隔
                        int delay = 1 << order;
                        System.err.println(String.format("连接失败，客户端第%d次重连", order));

                        bootstrap.config().group().schedule(() -> {
                            connect(bootstrap, host, port, retry - 1);
                        }, delay, TimeUnit.SECONDS);
                    }
                });
    }

    /**
     * 启动控制台输入
     * @param channel
     */
    private static void startConsoleThread(Channel channel) {
        Scanner sc = new Scanner(System.in);

        new Thread(() -> {
            while (!Thread.interrupted()) {

                if (!SessionUtil.hasLogin(channel)) {
                    // 未登录，先登录
                    System.out.println("请输入用户名：");
                    String userName = sc.nextLine();
                    // 发送登录消息包
                    channel.writeAndFlush(new LoginRequestPacket(userName, "pwd"));
                    // 等待响应
                    waitForLoginResponse();
                    System.out.println("输入消息发送到服务端：格式为userId message");
                } else {
                    // 会话开启，发送消息
                    String toUserId = sc.next();
                    String message = sc.next();
                    // 发送聊天消息包
                    channel.writeAndFlush(new MessageRequestPacket(toUserId, message));
                }
            }
        }).start();
    }

    /**
     *
     */
    private static void waitForLoginResponse() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
